#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import logging
import json

import pika
import tornado

from pika import adapters


class TornadoPublisher(object):
    EXCHANGE = ''
    EXCHANGE_TYPE = 'direct'
    QUEUE_TPL = '{}-rpc-queue'
    ROUTING_KEY = None

    def __init__(self, name, amqp_url):
        self._name = name
        self._url = amqp_url
        self._queue_name = self.QUEUE_TPL.format(self._name)
        self._conn = None
        self._chann = None

        self._deliveries = None
        self._acked = None
        self._nacked = None
        self._message_number = None

        self._stopping = False


    def connect(self):
        return adapters.TornadoConnection(pika.URLParameters(self._url),
                                          on_open_callback=self.on_connection_open,
                                          on_close_callback=self.on_connection_closed,
                                          stop_ioloop_on_close=False)

    def on_connection_open(self, unused_connection):
        self.open_channel()

    def on_connection_closed(self, connection, reply_code, reply_text):
        self._chann = None
        if self._stopping:
            self._conn.ioloop.stop()
        else:
            self._conn.add_timeout(5, self._conn.ioloop.stop)

    def open_channel(self):
        self._conn.channel(on_open_callback=self.on_channel_open)

    def on_channel_open(self, channel):
        self._chann = channel
        self.add_on_channel_close_callback()
        # self.setup_exchange(self.EXCHANGE)
    
    def add_on_channel_close_callback(self):
        self._chann.add_on_close_callback(self.on_channel_closed)

    def on_channel_closed(self, channel, reply_code, reply_text):
        self._chann = None
        if not self._stopping:
            self._conn.close()

    def setup_exchange(self, exchange_name):
        self._chann.exchange_declare(self.on_exchange_declareok, exchange_name, self.EXCHANGE_TYPE)

    def on_exchange_declareok(self, unused_frame):
        self.setup_queue(self._queue_name)

    def setup_queue(self, queue_name):
        self._chann.queue_declare(self.on_queue_declareok, queue_name)

    def on_queue_declareok(self, unused_frame):
        self._chann.queue_bind(self.on_bindok, self._queue_name, self.EXCHANGE, self.ROUTING_KEY)

    def on_bindok(self, unused_frame):
        self.start_pushlishing()

    def start_pushlishing(self):
        self.enable_delivery_confirmations()
        # self.schedule_next_message()

    def enable_delivery_confirmations(self):
        self._chann.confirm_delivery(self.on_delivery_confirmation)

    def on_delivery_confirmation(self, method_frame):
        confirmation_type = method_frame.method.NAME.split('.')[1].lower()
        if confirmation_type == 'ack':
            self._acked += 1
        elif confirmation_type == 'nack':
            self._nacked += 1
        self._deliveries.remove(method_frame.method.delivery_tag)

    # def schedule_next_message(self):
    #     self._conn.add_timeout(self.PUBLISH_INTERVAL, self.publish_message)

    def publish_message(self, message):
        if self._chann is None or not self._chann.is_open:
            return

        # properties = pika.BasicProperties(app_id='example-publisher', content_type='application/json', header=)
        self._chann.basic_publish(self.EXCHANGE, self.ROUTING_KEY, json.dumps(message, ensure_ascii=False))
        self._message_number += 1
        self._deliveries.append(self._message_number)
        # self.schedule_next_message()

    def run(self):
        while not self._stopping:
            self._conn = None
            self._deliveries = []
            self._acked = 0
            self._nacked = 0
            self._message_number = 0

            try:
                self._conn = self.connect()
                self._conn.ioloop.start()
            except KeyboardInterrupt:
                self.stop()
                if (self._conn is not None and not self._conn.is_closed()):
                    self._conn.ioloop.start()

    def start(self):
        self._conn = None
        self._deliveries = []
        self._acked = 0
        self._nacked = 0
        self._message_number = 0

        self._conn = self.connect()

    def stop(self):
        self._stopping = True
        self.close_channel()
        self.close_connnection()
    
    def close_channel(self):
        if self._chann is not None:
            self._chann.close()
    
    def close_connnection(self):
        if self._conn is not None:
            self._conn.close()
